//------------------------------------------------------------------------
// File:   md2c.c
// Author: Tony Saveski, tony@ui.com.au
//------------------------------------------------------------------------
// THIS SOFTWARE IS BEING PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT
// LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
// INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
// THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THE SOFTWARE.
//------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>

//------------------------------------------------------------------------
#pragma pack(1)

typedef struct
{
	int ident;		// magic number. must be equal to "IPD2"
	int version;	// md2 version. must be equal to 8

	int skinwidth;	// width of the texture
	int skinheight;	// height of the texture
	int framesize;	// size of one frame in bytes

	int num_skins;	// number of textures
	int num_xyz;	// number of vertices
	int num_st;		// number of texture coordinates
	int num_tris;	// number of triangles
	int num_glcmds;	// number of opengl commands
	int num_frames;	// total number of frames

	int ofs_skins;	// offset to skin names (64 bytes each)
	int ofs_st;		// offset to s-t texture coordinates
	int ofs_tris;	// offset to triangles
	int ofs_frames;	// offset to frame data
	int ofs_glcmds;	// offset to opengl commands
	int ofs_end;	// offset to end of file
} md2_header_t;

typedef struct
{
	unsigned char x, y, z, n;
} md2_vertex_t;

typedef struct
{
	short s, t;
} md2_texcoord_t;

typedef struct
{
	short i_xyz[3];
	short i_st[3];
} md2_triangle_t;

typedef struct
{
	float sx, sy, sz; // scale
	float tx, ty, tz; // translate
	char name[16];
	md2_vertex_t v[1]; // first vertex
} md2_frame_t;

//------------------------------------------------------------------------
FILE *g_fh;
md2_header_t g_header;
md2_vertex_t *g_xyz;
md2_texcoord_t *g_st;

//------------------------------------------------------------------------
void usage(void)
{
	fprintf(stderr, "Usage: md2c.exe <md2file>\n");
	exit(1);
}

//------------------------------------------------------------------------
void read_header(void)
{
	fread(&g_header, sizeof(md2_header_t), 1, g_fh);

	if(g_header.ident != 0x32504449)
	{
		fprintf(stderr, "ERROR: file identification string is not correct - '%x'\n", g_header.ident);
		exit(1);
	}

	if(g_header.version != 8)
	{
		fprintf(stderr, "ERROR: md2 version must be 8 - '%d'\n", g_header.version);
		exit(1);
	}
}

//------------------------------------------------------------------------
void write_header(char *fname)
{
	fprintf(stdout, "//-----------------------------------------\n");
	fprintf(stdout, "// Generated by MD2C.EXE\n");
	fprintf(stdout, "// Filename='%s'\n", fname);
	fprintf(stdout, "//-----------------------------------------\n");
	fprintf(stdout, "// Copy this into your code:\n");
	fprintf(stdout, "//\n");
	fprintf(stdout, "// typedef struct\n");
	fprintf(stdout, "// {\n");
	fprintf(stdout, "// 	short i_xyz[3];\n");
	fprintf(stdout, "// 	short i_st[3];\n");
	fprintf(stdout, "// } md2_triangle_t;\n");
	fprintf(stdout, "//\n");
	fprintf(stdout, "// extern int md2_num_xyz;\n");
	fprintf(stdout, "// extern int md2_num_st;\n");
	fprintf(stdout, "// extern int md2_num_tris;\n");
	fprintf(stdout, "// extern float md2_xyz[];\n");
	fprintf(stdout, "// extern float md2_st[];\n");
	fprintf(stdout, "// extern md2_triangle_t md2_tris[];\n");
	fprintf(stdout, "//-----------------------------------------\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "typedef struct\n");
	fprintf(stdout, "{\n");
	fprintf(stdout, "	short i_xyz[3];\n");
	fprintf(stdout, "	short i_st[3];\n");
	fprintf(stdout, "} md2_triangle_t;\n");
	fprintf(stdout, "\n");
}

//------------------------------------------------------------------------
void write_xyz(void)
{
void *buf;
md2_frame_t *frame;
int size, i;

	size = (g_header.num_xyz-1)*sizeof(md2_vertex_t) + sizeof(md2_frame_t);
	buf = malloc(size);
	if(!buf)
	{
		fprintf(stderr, "ERROR: failed to allocate memory for frame buffer\n");
		exit(1);
	}

	fseek(g_fh, g_header.ofs_frames, SEEK_SET);
	fread(buf, size, 1, g_fh);
	frame = buf;

	fprintf(stdout, "//-----------------------------------------\n");
	fprintf(stdout, "int md2_num_xyz = %d;\n", g_header.num_xyz);
	fprintf(stdout, "float md2_xyz[] = {\n");

	for(i=0; i<g_header.num_xyz; i++)
	{
		fprintf(stdout, "\t%ff,\t%ff,\t%ff,\t\t// v %d\n", 
			(float)(frame->v[i].x) * frame->sx + frame->tx,
			(float)(frame->v[i].y) * frame->sy + frame->ty,
			(float)(frame->v[i].z) * frame->sz + frame->tz,
			i);
	}

	fprintf(stdout, "};\n");
	fprintf(stdout, "\n");

	free(buf);
}

//------------------------------------------------------------------------
void write_st(void)
{
md2_texcoord_t *buf;
int size, i;

	size = sizeof(md2_texcoord_t)*g_header.num_st;
	buf = (md2_texcoord_t *)malloc(size);
	if(!buf)
	{
		fprintf(stderr, "ERROR: failed to allocate memory for texture coordinate buffer\n");
		exit(1);
	}
	
	fseek(g_fh, g_header.ofs_st, SEEK_SET);
	fread(buf, size, 1, g_fh);

	fprintf(stdout, "//-----------------------------------------\n");
	fprintf(stdout, "int md2_num_st = %d;\n", g_header.num_st);
	fprintf(stdout, "float md2_st[] = {\n");

	for(i=0; i<g_header.num_st; i++)
	{
		fprintf(stdout, "\t%ff,\t%ff,\t\t// tc %d\n", 
			(float)(buf[i].s) / g_header.skinwidth,
			(float)(buf[i].t) / g_header.skinheight,
			i);
	}

	fprintf(stdout, "};\n");
	fprintf(stdout, "\n");

	free(buf);
}

//------------------------------------------------------------------------
void write_tris(void)
{
md2_triangle_t *buf;
int size, i;

	size = sizeof(md2_triangle_t)*g_header.num_tris;
	buf = (md2_triangle_t *)malloc(size);
	if(!buf)
	{
		fprintf(stderr, "ERROR: failed to allocate memory for triangle buffer\n");
		exit(1);
	}
	
	fseek(g_fh, g_header.ofs_tris, SEEK_SET);
	fread(buf, size, 1, g_fh);

	fprintf(stdout, "//-----------------------------------------\n");
	fprintf(stdout, "int md2_num_tris = %d;\n", g_header.num_tris);
	fprintf(stdout, "md2_triangle_t md2_tris[] = {\n");

	for(i=0; i<g_header.num_tris; i++)
	{
		fprintf(stdout, "\t{{%d, %d, %d},\t{%d, %d, %d}},\t\t// tri %d\n", 
			buf[i].i_xyz[0], buf[i].i_xyz[1], buf[i].i_xyz[2],
			buf[i].i_st[0],  buf[i].i_st[1],  buf[i].i_st[2],
			i);
	}

	fprintf(stdout, "};\n");
	fprintf(stdout, "\n");

	free(buf);
}

//------------------------------------------------------------------------
int main(int argc, char **argv)
{
	if(argc!=2) usage();

	g_fh=fopen(argv[1], "rb");
	if(!g_fh)
	{
		fprintf(stderr, "ERROR: could not open file '%s'\n", argv[1]);
		exit(1);
	}

	read_header();

	write_header(argv[1]);
	write_xyz();
	write_st();
	write_tris();

	free(g_xyz);
	fclose(g_fh);
}
